package com.chaoyer.cynet.common.context;

import com.alibaba.fastjson.annotation.JSONField;
import com.chaoyer.cynet.common.constant.SysErrors;
import com.chaoyer.cynet.common.exception.CyNetException;
import com.google.common.collect.Maps;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class CyNetContext {

    /**
     * channel
     */
    @JSONField(serialize = false)
    public ChannelHandlerContext ctx;

    /**
     * HttpRequest 对象(包含请求headers,body)
     */
    @JSONField(serialize = false)
    public FullHttpRequest freq;

    @JSONField(serialize = false)
    public FullHttpResponse resp;
    /**
     * url
     */
    public String url;

    /**
     * url地址解析字段
     * example:
     * url:http://www.chaoyer.com/login?username=lwc&pass=123456
     * result:{username:lwc,pass:123456}
     */
    private Map<String, List<Object>> query;

    /**
     * body内容解析字段
     * example:
     * content-type:application/json
     * body:{username:lwc,pass:123456}
     * result:{username:lwc,pass123456}
     * or
     * content-type:x-www-form-urlencoded
     * body: &username=lwc&pass=123456
     * result:{username:lwc,pass:123456}
     */
    private Map<String, List<Object>> body;

    public CyNetContext() {
    }

    @SuppressWarnings("unchecked")
    public CyNetContext(ChannelHandlerContext ctx, FullHttpRequest freq, FullHttpResponse resp, Map query, Map<String, List<Object>> body) {
        this.ctx = ctx;
        this.freq = freq;
        this.resp = resp;
        this.url = freq.uri();
        this.query = query;
        this.body = body;
    }

    public Param queryParam(String key) throws CyNetException {
        return queryParam(key, false);
    }

    public Param queryParam(String key, boolean require) throws CyNetException {
        List<Object> result = this.query.get(key);

        if (result == null && !require) {
            throw new CyNetException(SysErrors.PARAM_NOT_CORRECT);
        }

        if (result == null || result.isEmpty()) {
            return null;
        }

        return new Param((String) result.get(0));

    }


    public List<Object> listQuery(String key) {
        return listQuery(key, false);
    }

    public List<Object> listQuery(String key, boolean nullAble) {
        List<Object> result = this.query.get(key);

        if (result == null && !nullAble) {
            throw new CyNetException(SysErrors.PARAM_NOT_CORRECT);
        }

        return result;
    }

    public Param bodyParam(String key) throws CyNetException {
        return bodyParam(key, false);
    }

    public Param bodyParam(String key, boolean nullAble) throws CyNetException {
        List<Object> result = this.body.get(key);

        if (result == null && !nullAble) {
            throw new CyNetException(SysErrors.PARAM_NOT_CORRECT);
        }

        if (result == null || result.isEmpty()) {
            return null;
        }

        return new Param((String) result.get(0));
    }

    public List<Object> listBody(String key) {
        return listQuery(key, false);
    }

    public List<Object> listBody(String key, boolean nullAble) {

        List<Object> result = this.body.get(key);

        if (result == null && !nullAble) {
            throw new CyNetException(SysErrors.PARAM_NOT_CORRECT);
        }

        return result;
    }

    public Map<String, Object> allParam() {

        Map<String, Object> params = Maps.newHashMap();
        merge(params, query);
        merge(params, body);

        return params;
    }


    private void merge(Map<String, Object> params, Map<String, List<Object>> obj) {
        for (Map.Entry<String, List<Object>> entry : obj.entrySet()) {
            List<Object> val = entry.getValue();
            if (null != val && val.size() <= 1) {
                if (val.size() == 0) {
                    params.put(entry.getKey(), null);
                } else {
                    params.put(entry.getKey(), entry.getValue().get(0));
                }
            }
            if (null != val && val.size() > 1) {
                params.put(entry.getKey(), entry.getValue());
            }
        }
    }

    public Map<String, Object> getQuery() {
        HashMap<String, Object> map = new HashMap<>();
        merge(map, query);
        return map;
    }

    public Map<String, Object> getBody() {
        HashMap<String, Object> map = new HashMap<>();
        merge(map, body);
        return map;
    }

    public void setQuery(Map<String, List<Object>> query) {
        this.query = query;
    }

    public void setBody(Map<String, List<Object>> body) {
        this.body = body;
    }

    @Override
    public String toString() {
        return "url:" + url +
                ",query:" + query +
                ", body:" + body;
    }
}
